package edu.ufl.cise.cop5555.sp12.codegen;

import javax.swing.text.StyledEditorKit.BoldAction;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import edu.ufl.cise.cop5555.sp12.Kind;
import edu.ufl.cise.cop5555.sp12.ast.ASTVisitor;
import edu.ufl.cise.cop5555.sp12.ast.AssignExprCommand;
import edu.ufl.cise.cop5555.sp12.ast.AssignPairListCommand;
import edu.ufl.cise.cop5555.sp12.ast.BinaryOpExpression;
import edu.ufl.cise.cop5555.sp12.ast.Block;
import edu.ufl.cise.cop5555.sp12.ast.Type;
import edu.ufl.cise.cop5555.sp12.ast.BooleanLiteralExpression;
import edu.ufl.cise.cop5555.sp12.ast.CompoundType;
import edu.ufl.cise.cop5555.sp12.ast.DecOrCommand;
import edu.ufl.cise.cop5555.sp12.ast.Declaration;
import edu.ufl.cise.cop5555.sp12.ast.DoCommand;
import edu.ufl.cise.cop5555.sp12.ast.DoEachCommand;
import edu.ufl.cise.cop5555.sp12.ast.ExprLValue;
import edu.ufl.cise.cop5555.sp12.ast.IfCommand;
import edu.ufl.cise.cop5555.sp12.ast.IfElseCommand;
import edu.ufl.cise.cop5555.sp12.ast.IntegerLiteralExpression;
import edu.ufl.cise.cop5555.sp12.ast.LValueExpression;
import edu.ufl.cise.cop5555.sp12.ast.Pair;
import edu.ufl.cise.cop5555.sp12.ast.PairList;
import edu.ufl.cise.cop5555.sp12.ast.PrintCommand;
import edu.ufl.cise.cop5555.sp12.ast.PrintlnCommand;
import edu.ufl.cise.cop5555.sp12.ast.Program;
import edu.ufl.cise.cop5555.sp12.ast.SimpleLValue;
import edu.ufl.cise.cop5555.sp12.ast.SimpleType;
import edu.ufl.cise.cop5555.sp12.ast.StringLiteralExpression;
import edu.ufl.cise.cop5555.sp12.ast.UnaryOpExpression;



public class CodeGenVisitor  implements ASTVisitor, Opcodes {
	
	

	String className;
	ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
	FieldVisitor fv;
	MethodVisitor mv;
	AnnotationVisitor av0;
	static int tempCount = 0;
	static int setCount=0;
	static int iterCount=0;
	static int entryCount=0;
	
	

	@Override
	public Object visitProgram(Program program, Object arg) throws Exception {
		className = program.ident.getText();
		cw.visit(V1_7, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", null);
		mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
		mv.visitCode();
		//set label on first instruction of main method
		Label lstart = new Label();
		mv.visitLabel(lstart);
		//visit block to generate code and field declarations
		program.block.visit(this,null);
		//add return instruction
		mv.visitInsn(RETURN);
		Label lend= new Label();
		mv.visitLabel(lend);
		//visit local variable--the only one in our project is the String[] argument to the main method
		mv.visitLocalVariable("args", "[Ljava/lang/String;", null, lstart, lend, 0);
		mv.visitMaxs(2, 1);
		mv.visitEnd();
		cw.visitEnd();
		//convert class file to byte array and return
		return cw.toByteArray();
	}

	@Override
	public Object visitBlock(Block block, Object arg) throws Exception {
		//visit children
		for (DecOrCommand cd : block.decOrCommands) {
			cd.visit(this, null);
		}
		return null;
	}

	@Override
	public Object visitDeclaration(Declaration declaration, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		
		String type= (String) declaration.type.visit(this, null);
		String ident = declaration.ident.getText();
		fv = cw.visitField(ACC_STATIC, ident+declaration.ident.scopeNum, type, null, null);
		if(type == "Ljava/util/HashMap;")
		{
			
			mv.visitTypeInsn(NEW, "java/util/HashMap");
			mv.visitInsn(DUP);
			mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
			mv.visitFieldInsn(PUTSTATIC, className, ident+declaration.ident.scopeNum, "Ljava/util/HashMap;");
		}
		fv.visitEnd();
		return null;
	}

	@Override
	public Object visitSimpleType(SimpleType simpleType, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		if(simpleType.type == Kind.INT)
			return "I";
		else if (simpleType.type == Kind.STRING)
			return "Ljava/lang/String;";
		else if(simpleType.type == Kind.BOOLEAN)
			return "Z";
		return null;
	}

	@Override
	public Object visitCompoundType(CompoundType compoundType, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		return "Ljava/util/HashMap;";
		
	}

	@Override
	public Object visitAssignExprCommand(AssignExprCommand assignExprCommand,
			Object arg) throws Exception {
		
		if(assignExprCommand.lValue instanceof  SimpleLValue && !(assignExprCommand.expression instanceof LValueExpression ))
		{
			//Simple case x = 2 etc
		String type = (String) assignExprCommand.expression.visit(this, arg);
		String name = (String) assignExprCommand.lValue.visit(this, arg);
		mv.visitFieldInsn(PUTSTATIC, className, name , type);
		return null;
		}
		
		else if(assignExprCommand.lValue instanceof  SimpleLValue && assignExprCommand.expression instanceof LValueExpression )
		{
			//case : y = m[1]..use get method
			String type = (String) assignExprCommand.expression.visit(this, arg);
			String name = (String) assignExprCommand.lValue.visit(this, arg);
			mv.visitFieldInsn(PUTSTATIC, className, name , type);
			return null;
			
		}
		else 
		{
			//m[1] = 3
			//If it is an instance of ExprLvalue 
			String name = (String)assignExprCommand.lValue.visit(this, arg);
			String expr = (String)assignExprCommand.expression.visit(this, arg);
			box(mv,expr);
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
			mv.visitInsn(POP);
			
			return null;
		}
	}

	@Override
	public Object visitAssignPairListCommand(
			AssignPairListCommand assignPairListCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		String name = (String)assignPairListCommand.lValue.visit(this, arg);
		mv.visitFieldInsn(GETSTATIC, className, name, "Ljava/util/HashMap;");
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "clear", "()V");
		mv.visitFieldInsn(GETSTATIC, className, name, "Ljava/util/HashMap;");
		String tempName = (String) assignPairListCommand.pairList.visit(this, arg);
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "putAll", "(Ljava/util/Map;)V");
	
		return null;
	}

	@Override
	public Object visitPrintCommand(PrintCommand printCommand, Object arg)
			throws Exception {
		//TODO Fix this to work with other types
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		String type = (String) printCommand.expression.visit(this,arg);
		if(type == "Ljava/util/HashMap;")
		{
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "(Ljava/lang/Object;)V");
			
		}
		else
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "print", "("+type+")V");
		
		return null;
	}

	@Override
	public Object visitPrintlnCommand(PrintlnCommand printlnCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
		String type = (String) printlnCommand.expression.visit(this,arg);
		if(type == "Ljava/util/HashMap;")
		{
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
			
		}
		else
		mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "("+type +")V");			
		return null;
		
	}

	@Override
	public Object visitDoCommand(DoCommand doCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		
		Label l1 = new Label();
		Label l2 = new Label();
		
		
		
		mv.visitLabel(l2);
		doCommand.expression.visit(this, arg); // evaluate expression
		mv.visitJumpInsn(IFNE, l1); // if true then execute block - goto label l1
		
		Label l3 = new Label();
		mv.visitJumpInsn(GOTO, l3);
		
		mv.visitLabel(l1);
		doCommand.block.visit(this, arg);
		mv.visitJumpInsn(GOTO, l2);
		
		mv.visitLabel(l3);
		return null;
	}

	public String getKeyType(Type t)
	{
		CompoundType ct;
		String keyType = "";
		
		if(t instanceof CompoundType)
		{
			ct = (CompoundType)t;
			keyType = getType(ct.keyType);
			
			
			
		}
		return keyType;
	}
	
	public String getValType(Type t)
	{
		CompoundType ct;
		String valType = "";
		
		if(t instanceof CompoundType)
		{
			ct = (CompoundType)t;
			valType = getType(ct.valType);
			
			
			
		}
		return valType;
		
	}
	
	public String createTempSet()
	{
		String name = "set"+setCount++;
		fv = cw.visitField(ACC_STATIC, name, "Ljava/util/Set;", null, null);
		return name;
		
	}
	public String createTempIter()
	{
		String name = "iter"+iterCount++;
		fv = cw.visitField(ACC_STATIC, name, "Ljava/util/Iterator;", null, null);
		return name;
		
	}
	public String createTempEntry()
	{
		String name = "entry"+entryCount++;
		fv = cw.visitField(ACC_STATIC, name, "Ljava/util/Map$Entry;", null, null);
		return name;
		
	}

	public String createTempHashMap()
	{
		String name = "temp"+tempCount++;
		fv = cw.visitField(ACC_STATIC, name, "Ljava/util/HashMap;", null, null);
		mv.visitTypeInsn(NEW, "java/util/HashMap");
		mv.visitInsn(DUP);
		mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
		mv.visitFieldInsn(PUTSTATIC, className, name, "Ljava/util/HashMap;");
		return name;
		
		
	}
	
	private String getType(Type t) {
		// TODO Auto-generated method stub
		SimpleType intType = new SimpleType(Kind.INT);
		SimpleType boolType = new SimpleType(Kind.BOOLEAN);
		SimpleType strType = new SimpleType(Kind.STRING);
		if(t.equals(intType))
			return "I";
		else if (t.equals(boolType))
			return "Z";
		else if(t.equals(strType))
			return "Ljava/lang/String;";
		else return "Ljava/util/HashMap;";
	}

	private void box(MethodVisitor mv, String expr) {
		if(expr == "I")
		{
			
			mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
			
		}
		else if(expr == "Z")
		{
			
			mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
			
			
		}
		
		
	}
	
	private void unbox(MethodVisitor mv, String type) {
		
		
			if (type.equals("I")) {
			mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue","()I");
			}
			if (type.equals("Z")) {
			mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean","booleanValue", "()Z");
			}
			if (type.equals("Ljava/lang/String;")) {
			mv.visitTypeInsn(CHECKCAST, "java/lang/String");
			}
			if (type.equals("Ljava/util/HashMap;")) {
			
				
			mv.visitTypeInsn(CHECKCAST, "java/util/HashMap");
			}
		
		
	}
	private void instantiateHashMap(MethodVisitor mv) {
		mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
		
	}

	public Object visitDoEachCommand(DoEachCommand doEachCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
	String name= (String)doEachCommand.lValue.visit(this, arg);
	mv.visitFieldInsn(GETSTATIC, className, name, "Ljava/util/HashMap;");
	mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "entrySet", "()Ljava/util/Set;");
	String setname = createTempSet();
	mv.visitFieldInsn(PUTSTATIC, className, setname, "Ljava/util/Set;");
	
	mv.visitFieldInsn(GETSTATIC, className, setname, "Ljava/util/Set;");
	mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
	String itname = createTempIter();
	mv.visitFieldInsn(PUTSTATIC, className, itname, "Ljava/util/Iterator;");
	
	String entryName = createTempEntry();
	Label l1 = new Label();
	mv.visitJumpInsn(GOTO, l1);
	Label l2 = new Label();
	mv.visitLabel(l2);
	//Body	
	mv.visitFieldInsn(GETSTATIC, className, itname, "Ljava/util/Iterator;");
	mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
	//TODO add checkcast
	mv.visitTypeInsn(CHECKCAST, "java/util/Map$Entry");
	mv.visitFieldInsn(PUTSTATIC, className, entryName, "Ljava/util/Map$Entry;");

	
	mv.visitFieldInsn(GETSTATIC, className, entryName, "Ljava/util/Map$Entry;");
	mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map$Entry", "getKey", "()Ljava/lang/Object;");
	
	Type t = doEachCommand.lValue.typebase;
	String keyType = getKeyType(t);
	String valType = getValType(t);
	unbox(mv, keyType);
	mv.visitFieldInsn(PUTSTATIC, className, doEachCommand.key.getText()+doEachCommand.key.scopeNum, keyType);
	//Unbox it according to type
	
	
	mv.visitFieldInsn(GETSTATIC, className, entryName, "Ljava/util/Map$Entry;");
	mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map$Entry", "getValue", "()Ljava/lang/Object;");
	
	unbox(mv, valType);
	mv.visitFieldInsn(PUTSTATIC, className, doEachCommand.val.getText()+doEachCommand.val.scopeNum, valType);
	
	doEachCommand.block.visit(this, arg);
	
	mv.visitLabel(l1);
	mv.visitFieldInsn(GETSTATIC, className, itname, "Ljava/util/Iterator;");
	mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
	mv.visitJumpInsn(IFNE, l2);
	
	return null;
	}

	@Override
	public Object visitIfCommand(IfCommand ifCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
			ifCommand.expression.visit(this, arg);
			Label l1 = new Label();
			mv.visitJumpInsn(IFEQ, l1);
			ifCommand.block.visit(this, arg);
			
			mv.visitLabel(l1);
			return null;
			
	
	}

	@Override
	public Object visitIfElseCommand(IfElseCommand ifElseCommand, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		ifElseCommand.expression.visit(this, arg);
		Label l1 = new Label();
		mv.visitJumpInsn(IFEQ, l1);
		ifElseCommand.ifBlock.visit(this, arg);
		Label l2 = new Label();
		mv.visitJumpInsn(GOTO, l2);
		
		mv.visitLabel(l1);
		ifElseCommand.elseBlock.visit(this, arg);
		
		
		mv.visitLabel(l2);	
		return null;
	}

	@Override
	public Object visitSimpleLValue(SimpleLValue simpleLValue, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		
		return (simpleLValue.identifier.getText()+simpleLValue.identifier.getScope());
		
	}

	@Override
	public Object visitExprLValue(ExprLValue exprLValue, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		
		mv.visitFieldInsn(GETSTATIC, className, exprLValue.identifier.getText()+exprLValue.identifier.getScope(), "Ljava/util/HashMap;");
		String expr = (String)exprLValue.expression.visit(this, arg);
		box(mv,expr);
		return exprLValue.identifier.getText()+exprLValue.identifier.getScope();
	}
	
	@Override
	public Object visitPair(Pair pair, Object arg) throws Exception {
		// TODO Auto-generated method stub
	//Leave the value of the 2 expressions on top of the stack
		String type0 = (String)pair.expression0.visit(this, arg);
		box(mv,type0);
		String type1 = (String)pair.expression1.visit(this, arg);
		box(mv,type1);	
		return null;
	}

	
	@Override
	public Object visitPairList(PairList pairList, Object arg) throws Exception {
		// TODO Auto-generated method stub
	
		//Create temporary hash map
		String name = createTempHashMap();
		
		for(Pair p : pairList.pairs)
		{
			mv.visitFieldInsn(GETSTATIC, className, name, "Ljava/util/HashMap;");
			p.visit(this, arg);
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
			mv.visitInsn(POP);
			
		}
		
		mv.visitFieldInsn(GETSTATIC, className, name, "Ljava/util/HashMap;");
		
		
		
		
		return name;
	}

	@Override
	public Object visitLValueExpression(LValueExpression lValueExpression,
			Object arg) throws Exception {
		// TODO Auto-generated method stub
		String name = (String)lValueExpression.lValue.visit(this, arg);
		Type t = lValueExpression.typeBase;
		String strType = getType(t);
		if(lValueExpression.lValue instanceof SimpleLValue)
			mv.visitFieldInsn(GETSTATIC, className,name, strType); //Put the value of name on  the stack
		else
		{
			mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
			unbox(mv,strType);
		}
	return strType;
		
	
		
	}

	
	@Override
	public Object visitIntegerLiteralExpression(
			IntegerLiteralExpression integerLiteralExpression, Object arg)
			throws Exception {
		//gen code to leave value of literal on top of stack
		mv.visitLdcInsn(integerLiteralExpression.integerLiteral.getIntVal());
		return "I";
	}

	@Override
	public Object visitBooleanLiteralExpression(
			BooleanLiteralExpression booleanLiteralExpression, Object arg)
			throws Exception {
		
		// TODO Auto-generated method stub
		if(booleanLiteralExpression.booleanLiteral.getText().equals("true"))
			mv.visitLdcInsn(new Boolean(true));
		else if (booleanLiteralExpression.booleanLiteral.getText().equals("false"))
			mv.visitLdcInsn(new Boolean(false));
		return "Z";
	}

	@Override
	public Object visitStringLiteralExpression(
			StringLiteralExpression stringLiteralExpression, Object arg)
			throws Exception {
		// TODO Auto-generated method stub
		mv.visitLdcInsn(stringLiteralExpression.stringLiteral.getText());
		return "Ljava/lang/String;";
	}

	@Override
	public Object visitUnaryOpExpression(UnaryOpExpression unaryOpExpression,
			Object arg) throws Exception {
		// TODO Auto-generated method stub
		String t = (String)unaryOpExpression.expression.visit(this, arg);
		if(unaryOpExpression.op == Kind.MINUS)
		{
			//negate value on top of stack;
			mv.visitInsn(INEG);
			
		}
		else if(unaryOpExpression.op == Kind.NOT)
		{
		
			Label l2 = new Label();
			mv.visitJumpInsn(IFEQ, l2);
			mv.visitInsn(ICONST_0);
			Label l3 = new Label();
			mv.visitJumpInsn(GOTO, l3);
			
			mv.visitLabel(l2);
			mv.visitInsn(ICONST_1);
		
			mv.visitLabel(l3);
			
		}
		return t;
	}

	@Override
	public Object visitBinaryOpExpression(
			BinaryOpExpression binaryOpExpression, Object arg) throws Exception {
		// TODO Auto-generated method stub
		
		String binType = getType(binaryOpExpression.typeBase);
		
		if(binType == "I")
		{
			
			String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
			String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
			//Addition
			if(binaryOpExpression.op == Kind.PLUS) //PLUS
			{
				mv.visitInsn(IADD);
			    return "I";
			}
			if(binaryOpExpression.op == Kind.MINUS)
			{
				mv.visitInsn(ISUB);
			    return "I";
			}
			if(binaryOpExpression.op == Kind.TIMES)
			{
				mv.visitInsn(IMUL);
			    return "I";
			}
			if(binaryOpExpression.op == Kind.DIVIDE)
			{
				mv.visitInsn(IDIV);
			    return "I";
			}
		
			
		}
		else if (binType == "Ljava/lang/String;")
		{
			
			//Operator plus - string concatenation
			Type strTemp = new SimpleType(Kind.STRING);
			Type intTemp = new SimpleType(Kind.INT);
			Type boolTemp = new SimpleType(Kind.BOOLEAN);
			
			if(binaryOpExpression.op == Kind.PLUS)
			{
				
			if((binaryOpExpression.expression0.typeBase.equals(strTemp) && binaryOpExpression.expression1.typeBase.equals(intTemp)) || (binaryOpExpression.expression1.typeBase.equals(strTemp)) && binaryOpExpression.expression0.typeBase.equals(intTemp))
			{
				if(binaryOpExpression.expression0.typeBase.equals(strTemp))
				{
				String exp0Type = (String) binaryOpExpression.expression0.visit(this, arg);
				mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
				mv.visitInsn(DUP_X1);
				mv.visitInsn(SWAP);
				mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;");
				mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
				String exp1Type = (String)binaryOpExpression.expression1.visit(this, arg);
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "("+exp1Type+")Ljava/lang/StringBuilder;");
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
				}
				else
				{
					mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
					mv.visitInsn(DUP);
					String exp0Type = (String) binaryOpExpression.expression0.visit(this, arg);
					mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "("+exp0Type+")Ljava/lang/String;");
					mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
					String exp1Type = (String)binaryOpExpression.expression1.visit(this, arg);
					mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "("+exp1Type+")Ljava/lang/StringBuilder;");
					mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
					
					
					
					
				}
				
			}
			else
			{
				//both are strings
				binaryOpExpression.expression0.visit(this, arg);
				mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
				mv.visitInsn(DUP_X1);
				mv.visitInsn(SWAP);
				mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;");
				mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
				String exp1Type = (String)binaryOpExpression.expression1.visit(this, arg);
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "("+exp1Type+")Ljava/lang/StringBuilder;");
				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
				
			}
			return "Ljava/lang/String;";
		
			
			}
			
			
			
		}
		else if (binType == "Z")
		{
			
			
			if(binaryOpExpression.op == Kind.EQUALS)
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
			if(t0 == "I" || t0 == "Z")
			{
			
			Label l1 = new Label ();
			mv.visitJumpInsn(IF_ICMPNE, l1);
			Label l2 = new Label();
			mv.visitLabel(l2);
			mv.visitInsn(ICONST_1);
			Label l3 = new Label();
			mv.visitJumpInsn(GOTO, l3);
			mv.visitLabel(l1);
			mv.visitInsn(ICONST_0);			
			mv.visitLabel(l3);
			return "Z";
			}
			else if(t0 == "Ljava/lang/String;")
			{
				
					Label l1 = new Label();
					mv.visitJumpInsn(IF_ACMPNE, l1);
					mv.visitInsn(ICONST_1);
					Label l2 = new Label();
					mv.visitJumpInsn(GOTO, l2);
					
					mv.visitLabel(l1);
					mv.visitInsn(ICONST_0);
					
					mv.visitLabel(l2);
					return "Z";
				
			}
			else
			{
				//EQUALS - map
				mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "equals", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				
				
			}
			
			}
			
			if(binaryOpExpression.op == Kind.NOT_EQUALS)
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);	
				if(t0 == "I" || t0 == "Z")
				{
				Label l1 = new Label ();
				mv.visitJumpInsn(IF_ICMPEQ, l1);
				Label l2 = new Label();
				mv.visitLabel(l2);
				mv.visitInsn(ICONST_1);
				Label l3 = new Label();
				mv.visitJumpInsn(GOTO, l3);
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);			
				
				mv.visitLabel(l3);
				return "Z";
				}
				else if(t0 == "Ljava/lang/String;")
				{
					
						Label l1 = new Label();
						mv.visitJumpInsn(IF_ACMPEQ, l1);
						mv.visitInsn(ICONST_1);
						Label l2 = new Label();
						mv.visitJumpInsn(GOTO, l2);
						
						mv.visitLabel(l1);
						mv.visitInsn(ICONST_0);
						
						mv.visitLabel(l2);
						return "Z";
					
				}
				else
				{
					
					mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "not_equals", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				}
				
			}
			
			if(binaryOpExpression.op == Kind.LESS_THAN)
			{
				
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
				if(t0 == "I" || t0 == "Z")
				{
				Label l1 = new Label ();
				mv.visitJumpInsn(IF_ICMPGE, l1);
				Label l2 = new Label();
				mv.visitLabel(l2);
				mv.visitInsn(ICONST_1);
				Label l3 = new Label();
				mv.visitJumpInsn(GOTO, l3);
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);			
				
				mv.visitLabel(l3);
				return "Z";
				}
				else
				{
					mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "less_than", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				}
				
				
			}
			
			if(binaryOpExpression.op == Kind.GREATER_THAN)
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
				if(t0 == "I" || t0 == "Z")
				{
				Label l1 = new Label ();
				mv.visitJumpInsn(IF_ICMPLE, l1);
				Label l2 = new Label();
				mv.visitLabel(l2);
				mv.visitInsn(ICONST_1);
				Label l3 = new Label();
				mv.visitJumpInsn(GOTO, l3);
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);			
				
				mv.visitLabel(l3);
				return "Z";
				}else
				{
					mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "greater_than", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				}
				
			}
			if(binaryOpExpression.op == Kind.AT_LEAST) // >=
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
				if(t0 == "I" || t0 == "Z")
				{
				Label l1 = new Label ();
				mv.visitJumpInsn(IF_ICMPLT, l1);
				Label l2 = new Label();
				mv.visitLabel(l2);
				mv.visitInsn(ICONST_1);
				Label l3 = new Label();
				mv.visitJumpInsn(GOTO, l3);
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);			
				
				mv.visitLabel(l3);
				return "Z";
				}
				else
				{
					mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "at_least", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				}
				
			}
			if(binaryOpExpression.op == Kind.AT_MOST) //<=
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
				if(t0 == "I" || t0 == "Z")
				{
					
				Label l1 = new Label ();
				mv.visitJumpInsn(IF_ICMPGT, l1);
				Label l2 = new Label();
				mv.visitLabel(l2);
				mv.visitInsn(ICONST_1);
				Label l3 = new Label();
				mv.visitJumpInsn(GOTO, l3);
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);			
				
				mv.visitLabel(l3);
				return "Z";
				}
				else if(t0 == "Ljava/lang/String;")
				{
					mv.visitInsn(SWAP);
					mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "startsWith", "(Ljava/lang/String;)Z");
					return "Z";
					//String
					
				}
				else
				{
					mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "at_most", "(Ljava/util/HashMap;Ljava/util/HashMap;)Z");
				}
				
			}
			
			if(binaryOpExpression.op == Kind.AND)
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				
				if(t0 == "Z" || t0 == "Z")
				{
				Label l1 = new Label();
				mv.visitJumpInsn(IFEQ, l1);
				String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
				mv.visitJumpInsn(IFEQ, l1);
				mv.visitInsn(ICONST_1);
				Label l2 = new Label ();
				mv.visitJumpInsn(GOTO, l2);
				
				mv.visitLabel(l1);
				mv.visitInsn(ICONST_0);
				
				mv.visitLabel(l2);
				return "Z";
					
					
				}
				
							
			}
			if(binaryOpExpression.op == Kind.OR)
			{
				String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
				if(t0 == "Z")
				{
					Label l1 = new Label();
					mv.visitJumpInsn(IFNE, l1);
					String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
					mv.visitJumpInsn(IFNE, l1);
					mv.visitInsn(ICONST_0);
					Label l2 = new Label ();
					mv.visitJumpInsn(GOTO, l2);
					
					
					mv.visitLabel(l1);
					mv.visitInsn(ICONST_1);
					
					mv.visitLabel(l2);
					return "Z";
					
					
				}
				
			}
			
			
			
		}
		else {
			//Output of the operations are Map
			//PLUS
			
		//	String tempMap = createTempHashMap();
			
			String t0 = (String)binaryOpExpression.expression0.visit(this, arg);
			String t1 = (String)binaryOpExpression.expression1.visit(this, arg);
			
			if(binaryOpExpression.op == Kind.PLUS) //PLUS
			{
				mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "plus", "(Ljava/util/HashMap;Ljava/util/HashMap;)Ljava/util/HashMap;");
				
			
			}
			else if (binaryOpExpression.op == Kind.MINUS)
			{
				mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "minus", "(Ljava/util/HashMap;Ljava/util/HashMap;)Ljava/util/HashMap;");
			}
			else if (binaryOpExpression.op == Kind.TIMES)
			{
				
				mv.visitMethodInsn(INVOKESTATIC, "edu/ufl/cise/cop5555/sp12/Runtime", "times", "(Ljava/util/HashMap;Ljava/util/HashMap;)Ljava/util/HashMap;");
			}
			
			
			
			
		}
		return  binType;
	}
	

}
